namespace KinonekoSoftware.CodariaDB.Documents.Templates
{
    public abstract class InstanceTemplateGenerator
    {
        private static readonly Dictionary<InstanceType, InstanceTemplateGenerator> _generators;

        static InstanceTemplateGenerator()
        {
            _generators = new Dictionary<InstanceType, InstanceTemplateGenerator>();

            AddHandler(new CharacterInstanceTemplateGenerator());
            AddHandler(new AbilityInstanceTemplateGenerator());
            AddHandler(new ClassInstanceTemplateGenerator());
            AddHandler(new CountryInstanceTemplateGenerator());
            AddHandler(new EquipmentInstanceTemplateGenerator());
            AddHandler(new GeographyInstanceTemplateGenerator());
            AddHandler(new ItemInstanceTemplateGenerator());
            AddHandler(new MaterialInstanceTemplateGenerator());
            AddHandler(new PlantInstanceTemplateGenerator());
            AddHandler(new ResourceInstanceTemplateGenerator());
            AddHandler(new SchoolInstanceTemplateGenerator());
            AddHandler(new SpeciesInstanceTemplateGenerator());
            AddHandler(new TeamInstanceTemplateGenerator());
            AddHandler(new ClubInstanceTemplateGenerator());
            AddHandler(new GroupInstanceTemplateGenerator());
            AddHandler(new GangsTeamInstanceTemplateGenerator());
            AddHandler(new KizunaInstanceTemplateGenerator());
            AddHandler(new WeaponInstanceTemplateGenerator());
        }

        #region Helper Methods

        public static void AddHandler(InstanceTemplateGenerator template)
        {
            if (template is null)
            {
                return;
            }

            _generators.TryAdd(template.TargetType, template);
        }

        public static CompositeParagraph Paragraph(string name, string metadata, bool removable = true, string value = null) => new CompositeParagraph
        {
            Id          = ID.Get(),
            IsRemovable = removable,
            Name        = name,
            Metadata    = metadata,
        };

        public static void Paragraph(ICollection<CompositeItem> collection, string name, string metadata, bool removable = true, string value = null)
        {
            collection.Add(new CompositeParagraph
            {
                Id          = ID.Get(),
                IsRemovable = removable,
                Name        = name,
                Metadata    = metadata,
                Value       = value,
            });
        }

        public static void Property(ICollection<CompositeItem> collection, string name, string metadata, bool removable = true, string value = null)
        {
            collection.Add(new CompositeProperty
            {
                Id          = ID.Get(),
                Name        = name,
                Metadata    = metadata,
                IsRemovable = removable,
                IsEditable  = removable,
                Value       = value,
            });
        }

        public static CompositeProperty Property(string name, string metadata, bool removable = true, string value = null) => new CompositeProperty
        {
            Id          = ID.Get(),
            Name        = name,
            Metadata    = metadata,
            IsRemovable = removable,
            IsEditable  = removable,
            Value       = value,
        };


        public static void ToggleSwitch(ICollection<CompositeItem> collection, string name, string metadata, bool removable = true, bool value = false)
        {
            collection.Add(new CompositeToggleSwitch
            {
                Id          = ID.Get(),
                IsRemovable = removable,
                Name        = name,
                Metadata    = metadata,
                Value       = value,
            });
        }

        public static CompositeToggleSwitch ToggleSwitch(string name, string metadata, bool removable = true, bool value = false) => new CompositeToggleSwitch
        {
            Id          = ID.Get(),
            IsRemovable = removable,
            Name        = name,
            Metadata    = metadata,
            Value       = value,
        };

        public static void Define(ICollection<DataPartDefinition> collection, DataPartType type, int index)
        {
            collection.Add(new DataPartDefinition
            {
                Type  = type,
                Index = index,
            });
        }

        public static DataPartDefinition Define(DataPartType type, int index)
        {
            return new DataPartDefinition
            {
                Type  = type,
                Index = index,
            };
        }

        #endregion

        #region Override Methods

        /// <summary>
        /// 初始化模组集合。
        /// </summary>
        /// <param name="collection">要初始化的模组集合。</param>
        protected abstract void OnInitializeDefinitions(ICollection<DataPartDefinition> collection);


        /// <summary>
        /// 初始化属性集合。
        /// </summary>
        /// <param name="collection">要初始化的属性集合。</param>
        /// <remarks>
        /// <para>名字的字段由DataPart创建，而非模板自带。</para>
        /// </remarks>
        protected abstract void OnInitializeProperties(ICollection<CompositeItem> collection);

        /// <summary>
        /// 创建模板，默认实现了创建模板的逻辑。
        /// </summary>
        /// <returns>返回创建的模板。</returns>
        public virtual InstanceTemplate CreateTemplate()
        {
            var it = new InstanceTemplate
            {
                Id            = ID.Get(),
                UseCustomType = false,
                BuiltinType   = TargetType,
                Definitions   = new ObservableCollection<DataPartDefinition>(),
                Properties    = new ObservableCollection<CompositeItem>(),
            };

            OnInitializeDefinitions(it.Definitions);
            OnInitializeProperties(it.Properties);

            return it;
        }

        #endregion

        /// <summary>
        /// 尝试创建指定设定类型的模板。
        /// </summary>
        /// <param name="type">指定的设定类型。</param>
        /// <returns>返回一个新的设定类型的模板，如果查找不到指定的模板，则会创建人物设定的模板代替。</returns>
        public static InstanceTemplate Generate(InstanceType type)
        {
            var generator = _generators.TryGetValue(type, out var g) ? g : _generators[InstanceType.Character];
            return generator.CreateTemplate();
        }

        public abstract InstanceType TargetType { get; }
    }
}